package game;

import behaviors.Position;
import behaviors.Rotation;
import engine.hw.Keys;
import engine.hw.RunTimer;
import math.Maths;
import math.vector.Vecmath;
import math.vector.vec3f;

public class ViewMotionKey extends ViewMotion
{
    
    private static final int NUM_KEYS_BOUND = 6;
    
    private static final float MAX_SPEED = 8.0f;
    
    private static final float MAX_ACCEL = 1.0f;
    
    private static final vec3f VEC_FORW = Vecmath.getInvertedOf(Vecmath.AXIS_Z);
    private static final vec3f VEC_FORW_LEFT = new vec3f(
        -Maths.SQRT_2_OVER_2, 
        0.0f, 
        -Maths.SQRT_2_OVER_2
    );
    private static final vec3f VEC_LEFT = Vecmath.getInvertedOf(Vecmath.AXIS_X);
    private static final vec3f VEC_BACK_LEFT = new vec3f(
        -Maths.SQRT_2_OVER_2, 
        0.0f, 
        Maths.SQRT_2_OVER_2
    );
    private static final vec3f VEC_BACK = Vecmath.AXIS_Z;
    private static final vec3f VEC_BACK_RGHT = new vec3f(
        Maths.SQRT_2_OVER_2, 
        0.0f, 
        Maths.SQRT_2_OVER_2
    );
    private static final vec3f VEC_RGHT = Vecmath.AXIS_X;
    private static final vec3f VEC_FORW_RGHT = new vec3f(
        Maths.SQRT_2_OVER_2, 
        0.0f, 
        -Maths.SQRT_2_OVER_2
    );
    
    private static final int MOT_NONE = 0;
    private static final int MOT_FORW = 1;
    private static final int MOT_FORW_LEFT = 2;
    private static final int MOT_LEFT = 3;
    private static final int MOT_BACK_LEFT = 4;
    private static final int MOT_BACK = 5;
    private static final int MOT_BACK_RGHT = 6;
    private static final int MOT_RGHT = 7;
    private static final int MOT_FORW_RGHT = 8;
    
    public static final int KEY_FORW = 0;
    public static final int KEY_BACK = 1;
    public static final int KEY_LEFT = 2;
    public static final int KEY_RGHT = 3;
    public static final int KEY_DOWN = 4;
    public static final int KEY_UPWD = 5;
    
    
    private KeyBind[] keyBinds;
    //<editor-fold defaultstate="collapsed" desc="getKeyBinding">
    public KeyBind getKeyBinding(final int keyEnum)
    {
        int keyIndex = Maths.capi(keyEnum, 0, ViewMotionKey.NUM_KEYS_BOUND);
        return this.keyBinds[keyIndex];
    }
    //</editor-fold> // Index lookup method
    
    private vec3f dirDesired;
    
    public ViewMotionKey(
            Position viewPosition, 
            Rotation viewRotation)
    {
        super(
            viewPosition, 
            viewRotation
        );
        this.keyBinds = new KeyBind[ViewMotionKey.NUM_KEYS_BOUND];
        for (int i = 0; i < ViewMotionKey.NUM_KEYS_BOUND; i++)
        {
            this.keyBinds[i] = new KeyBind();
        }
        this.dirDesired = new vec3f();
    }
    
    public void update() 
    {
        float time = RunTimer.get().getFrameTime();
        
        this.chooseDesiredDirection();
        
        vec3f rotatedDir = Vecmath.getRotationOf( // Rotate gamepad inputs
            this.dirDesired, // Source vector
            Vecmath.AXIS_Y, // Axis vector
            -this.viewRotation.getRots().y // AngleRad to rotate
        );
        rotatedDir.scale(ViewMotionKey.MAX_ACCEL);
        
//        this.motionVelocity.translate(rotatedDir);
//        this.motionVelocity.limitToLength(ViewMotionKey.MAX_SPEED);
//        float lengthSq = this.motionVelocity.getLengthSq();
//        float drag = (lengthSq != 0.0f ? lengthSq : 1.0f);
//        this.motionVelocity.scale(drag);
//        
//        Vecmath.calcScaledCopyOf( // Determine the motion distance vector this frame
//            this.motionVelocity.x, this.motionVelocity.y, this.motionVelocity.z, // Source
//            time, time, time, // Scale factors
//            this.motionDistance // Destination vector
//        );
        
        this.motionDistance.setToZero();
        this.motionDistance.translate(rotatedDir);
        this.motionDistance.scale(ViewMotionKey.MAX_SPEED * time);
        
        this.viewPosition.translateAdd(this.motionDistance);
    }
    
    private void chooseDesiredDirection()
    {
        boolean forw = Keys.get().isDown(this.keyBinds[ViewMotionKey.KEY_FORW].getBoundKey());
        boolean back = Keys.get().isDown(this.keyBinds[ViewMotionKey.KEY_BACK].getBoundKey());
        boolean left = Keys.get().isDown(this.keyBinds[ViewMotionKey.KEY_LEFT].getBoundKey());
        boolean rght = Keys.get().isDown(this.keyBinds[ViewMotionKey.KEY_RGHT].getBoundKey());
        int motionDirEnum = this.getMotionDirEnum(forw, back, rght, left);
        vec3f desiredDir = this.getMotionDirection(motionDirEnum);
        this.dirDesired.setTo(desiredDir);
        boolean down = Keys.get().isDown(this.keyBinds[ViewMotionKey.KEY_DOWN].getBoundKey());
        boolean upwd = Keys.get().isDown(this.keyBinds[ViewMotionKey.KEY_UPWD].getBoundKey());
        if (down)
        {
            this.dirDesired.y += -1.0f;
        }
        if (upwd)
        {
            this.dirDesired.y += 1.0f;
        }
    }
    
    private vec3f getMotionDirection(final int motionDir) 
    {
        switch (motionDir) 
        {
            case (MOT_NONE): 
            {
                return Vecmath.ORIGIN;
            } 
            case (MOT_FORW): 
            {
                return VEC_FORW;
            } 
            case (MOT_FORW_LEFT): 
            {
                return VEC_FORW_LEFT;
            } 
            case (MOT_LEFT): 
            {
                return VEC_LEFT;
            } 
            case (MOT_BACK_LEFT): 
            {
                return VEC_BACK_LEFT;
            } 
            case (MOT_BACK): 
            {
                return VEC_BACK;
            } 
            case (MOT_BACK_RGHT): 
            {
                return VEC_BACK_RGHT;
            } 
            case (MOT_RGHT): 
            {
                return VEC_RGHT;
            } 
            case (MOT_FORW_RGHT): 
            {
                return VEC_FORW_RGHT;
            } 
            default: 
            {
                return Vecmath.ORIGIN;
            }
        }
    }
    
    private int getMotionDirEnum(boolean forw, boolean back, boolean rght, boolean left) 
    {
        int motionDir = MOT_NONE;
        if (!forw || !back || !rght || !left) 
        {
            if (forw && back) 
            {
                if (rght) 
                {
                    motionDir = MOT_RGHT;
                } 
                else if (left) 
                {
                    motionDir = MOT_LEFT;
                }
            } 
            else if (rght && left) 
            {
                if (forw) 
                {
                    motionDir = MOT_FORW;
                } 
                else if (back) 
                {
                    motionDir = MOT_BACK;
                }
            } 
            else if (forw && rght) 
            {
                motionDir = MOT_FORW_RGHT;
            } 
            else if (back && rght) 
            {
                motionDir = MOT_BACK_RGHT;
            } 
            else if (back && left) 
            {
                motionDir = MOT_BACK_LEFT;
            } 
            else if (forw && left) 
            {
                motionDir = MOT_FORW_LEFT;
            } 
            else if (forw) 
            {
                motionDir = MOT_FORW;
            } 
            else if (back) 
            {
                motionDir = MOT_BACK;
            } 
            else if (rght) 
            {
                motionDir = MOT_RGHT;
            } 
            else if (left) 
            {
                motionDir = MOT_LEFT;
            }
        }
        return motionDir;
    }
    
    public void destroy()
    {
        for (int i = 0; i < ViewMotionKey.NUM_KEYS_BOUND; i++)
        {
            this.keyBinds[i].destroy();
            this.keyBinds[i] = null;
        }
        this.keyBinds = null;
    }
    
}